package com.kelvem.saas.workbanch.security.shiro;

import com.kelvem.saas.workbanch.core.exception.ErrorCodeEnum;
import com.kelvem.saas.workbanch.core.exception.SaasAssert;
import com.kelvem.saas.workbanch.core.exception.SaasException;
import com.kelvem.saas.workbanch.core.manager.dto.OrgUrlDto;
import com.kelvem.saas.workbanch.core.manager.service.GraphqlService;
import com.kelvem.saas.workbanch.security.shiro.encoder.BCryptPasswordEncoder;
import com.kelvem.saas.workbanch.core.system.model.SysUserEntity;
import com.kelvem.saas.workbanch.core.system.request.SysUserQueryVo;
import com.kelvem.saas.workbanch.core.system.service.SysUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * shiro 用户 Service
 *
 * @author kelvem
 * @version 1.0
 */
@Service
@Slf4j
public class ShiroUserService {

    @Resource
    private BCryptPasswordEncoder bcryptPasswordEncoder;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private GraphqlService graphqlService;


    public String login(String username, String password) {
        try {
            log.info("ShiroUserService:login:start, username=【{}】", username);
            SysUserEntity sysUser = sysUserService.getSysUserByUsername(username);
            if (sysUser == null || !bcryptPasswordEncoder.check(password, sysUser.getPassword())) {
                throw new SaasException(ErrorCodeEnum.LOGIN_FAIL);
            }
            // Generate JWT token here
            String jwtToken = JwtUtil.generateToken(sysUser);
            log.info("ShiroUserService:login:end");
            return jwtToken;
        } catch(Exception e) {
            log.info("ShiroUserService:login:err, login fail, username=【{}】", username);
            throw new SaasException(ErrorCodeEnum.LOGIN_FAIL, e);
        }
    }

    public SysUserEntity getUserByToken(String token) {
        log.info("ShiroUserService:getUserByToken:start, token=【{}】", token);
        SaasAssert.notNull(token);

        try {
            String username = JwtUtil.extractUsername(token);

            SysUserQueryVo sysUserQueryVo = SysUserQueryVo.builder().username(username).build();
            List<SysUserEntity> list = this.sysUserService.queryList(sysUserQueryVo);
            if (list.size() == 1) {
                SysUserEntity result = list.get(0);

                // todo page url
                log.info("ShiroUserService:getUserByToken:end, SysUser={}", result);
                return result;
            } else if (list.size() > 1) {
                log.warn("getUserByToken result too many, token={}, list.size={}", token, list.size());
            }
        } catch (Exception e) {
            String msg = String.format("ShiroUserService:getUserByToken:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
        // 未查询到或查询过多, 返回null
        return null;
    }

    /**
     * 获取所有组织和url的映射关系
     *
     * @return
     */
    private static List<OrgUrlDto> cacheOrgUrlList = null;

    public List<OrgUrlDto> queryAllOrgUrlList() {
        if (cacheOrgUrlList == null) {
            log.info("ShiroUserService:queryAllOrgUrlList");
            cacheOrgUrlList = sysUserService.queryAllOrgUrlList();
        }
        return cacheOrgUrlList;
    }

    public void expiredCacheOrgUrlMap() {
        log.info("ShiroUserService:expiredCacheOrgUrlMap");
        cacheOrgUrlList = null;
    }

    public Map<String, String> queryAllOrgUrlMap() {
        if (cacheOrgUrlList == null) {
            cacheOrgUrlList = sysUserService.queryAllOrgUrlList();
        }
        Map<String, List<String>> map = new LinkedHashMap<>();
        for (OrgUrlDto orgUrlDto : cacheOrgUrlList) {
            String url = orgUrlDto.getUrl();
            Long orgId = orgUrlDto.getOrgId();
            if (!map.containsKey(url)) {
                map.put(url, new ArrayList<>());
            }
            map.get(url).add(orgId.toString());
        }
        Map<String, String> result = new LinkedHashMap<>();
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            String url = entry.getKey();
            List<String> list = entry.getValue();
            result.put(url, String.join(",", list));
        }
        result.put("/bizPage/v1/queryMenuByToken", "default");
        return result;
    }

    public Set<String> filterOrgUrlByOrgId(Long orgId) {
        SaasAssert.notNull(orgId);
        if (cacheOrgUrlList == null) {
            cacheOrgUrlList = sysUserService.queryAllOrgUrlList();
        }

        Set<String> permissionSet = new HashSet<>();
        for (OrgUrlDto orgUrl : cacheOrgUrlList) {
            if (orgUrl.getOrgId().equals(orgId)) {
                permissionSet.add(orgUrl.getUrl());
            }
        }

        // 增加登录后默认权限
        permissionSet.add("/bizPage/v1/queryMenuByToken");
        return permissionSet;
    }

    public Integer authorizeOrgMenus(Long orgId, List<Long> bizPageIdList) {

        log.info("SysUserService:authorizeOrgMenus:start, orgId=【{}】, bizPageIdList=【{}】", orgId, bizPageIdList);
        SaasAssert.notNull(orgId);
        if (bizPageIdList == null) {
            bizPageIdList = new ArrayList<>();
        }

        try {
            // select
//            String sqlSelect = "select " + targetColumn + " as `target_id` "
//                    + " from sys_"
//                    + " where " + srcColumn + " = " + orgId;
//            Map<String, String> map = new HashMap<>();
//            map.put("sql", sqlSelect);
            List<Map<String, Object>> listMap = graphqlService.queryEntityList("sys_org_mapping_menu", Map.of("orgId", orgId));

            Map<Long, Long> dbMenuIdMap = new HashMap<>();
            for (Map<String, Object> row : listMap) {
                Long id = Long.parseLong(row.get("id").toString());
                Long dbMenuId = Long.parseLong(row.get("menuId").toString());
                dbMenuIdMap.put(dbMenuId, id);
            }

            // add
            for (Long menuId : bizPageIdList) {
                if (!dbMenuIdMap.containsKey(menuId)) {
                    // 不存在则添加
                    Map<String, Object> entity = new HashMap<>();
                    entity.put("orgId", orgId);
                    entity.put("menuId", menuId);
                    graphqlService.addEntity("sys_org_mapping_menu", entity);
                }
            }

            // delete
            for (Long dbMenuId : dbMenuIdMap.keySet()) {
                if (!bizPageIdList.contains(dbMenuId)) {
                    // db不存在则删除
                    Long id = dbMenuIdMap.get(dbMenuId);
                    graphqlService.deleteEntity("sys_org_mapping_menu", id);
                }
            }

            // update(无需该操作)

            // 最后失效安全框架缓存
            this.expiredCacheOrgUrlMap();

            return bizPageIdList.size();
        } catch (Exception e) {
            String msg = String.format("SysUserService:authorizeOrgMenus:error, %s", e.getMessage());
            log.error(msg, e);
            throw new SaasException(msg);
        }
    }

}